package cube.utils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashSet;
import java.util.Set;

import cube.data.BIMap;
import cube.data.util.DataUtil;
import cube.io.StreamUtil;
import cube.utils.exec.BeanInvoker;

/**
 * Bean对象工具
 * 
 * @author abiao
 */
public class BeanUtil {

    /**
     * 基本类型-封装类型双向映射
     */
    private static final BIMap<Class<?>, Class<?>> baseTypes = new BIMap<Class<?>, Class<?>>();

    /**
     * 初始化
     */
    static {
        baseTypes.put(byte.class, Byte.class);
        baseTypes.put(int.class, Integer.class);
        baseTypes.put(short.class, Short.class);
        baseTypes.put(long.class, Long.class);
        baseTypes.put(float.class, Float.class);
        baseTypes.put(double.class, Double.class);
        baseTypes.put(boolean.class, Boolean.class);
        baseTypes.put(char.class, Character.class);
    }

    /**
     * 映射指定属性
     * 
     * @param srcObj
     *            原始对象
     * @param dstObj
     *            目标对象
     * @param props
     *            映射属性
     */
    public static void autoSetProps(Object srcObj, Object dstObj,
            Set<String> props) {
        try {
            for (String prop : props) {
                mapObjectProp(srcObj, dstObj, prop);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 映射指定属性
     * 
     * @param srcObj
     *            原始对象
     * @param dstObj
     *            目标对象
     * @param props
     *            映射属性
     */
    public static void autoSetProps(Object srcObj, Object dstObj, String[] props) {
        try {
            for (int i = 0; i < props.length; i++) {
                mapObjectProp(srcObj, dstObj, props[i]);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 属性映射
     * 
     * @param srcObj
     *            原始对象
     * @param dstObj
     *            目标对象
     * @param prop
     *            映射属性
     * @throws Exception
     * @throws Exception
     *             异常
     */
    private static void mapObjectProp(Object srcObj, Object dstObj, String prop)
            throws Exception {
        Field srcField = getField(srcObj, prop);
        Object srcProp = srcField.get(srcObj);

        Field dstField = getField(dstObj, prop);
        dstField.set(dstObj, srcProp);
    }

    /**
     * 将对象序列化为字节数组
     */
    public static byte[] object2bytes(Object o) {
        byte[] objectBytes = null;
        try {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(o);
            oos.flush();
            objectBytes = bos.toByteArray();
            StreamUtil.close(oos, bos);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return objectBytes;
    }

    /**
     * 读取序列化的对象字节为对象
     */
    public static Object bytes2object(byte[] data) {
        Object object = null;
        try {
            ByteArrayInputStream bis = new ByteArrayInputStream(data);
            ObjectInputStream ois = new ObjectInputStream(bis);
            object = ois.readObject();
            StreamUtil.close(ois, bis);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return object;
    }

    /**
     * 对对象进行深拷贝操作
     */
    public static Object deepCopy(Object o) {
        return bytes2object(object2bytes(o));
    }

    /**
     * 获取类的所有属性
     * 
     * @param c
     *            类
     * @return 此类所有属性
     */
    public static Set<Field> getAllFields(Class<?> c) {
        Set<Field> fields = new HashSet<Field>();
        if (hasSuper(c)) {
            for (Field f : getAllFields(c.getSuperclass())) {
                fields.add(f);
            }
        }
        for (Field f : c.getDeclaredFields()) {
            fields.add(f);
            f.setAccessible(true);
        }
        return fields;
    }

    /**
     * 根据属性名获取属性
     * 
     * @param obj
     *            对象
     * @param fieldName
     *            属性名
     * @return 属性
     */
    public static Field getField(Object obj, String fieldName) {
        Field field = null;
        try {
            field = obj.getClass().getDeclaredField(fieldName);
        } catch (NoSuchFieldException e) {
            field = getSuperField(obj.getClass(), fieldName);
        }
        if (field != null) {
            field.setAccessible(true);
        }
        return field;
    }

    /**
     * 检查对象是否有此属性
     */
    public static boolean hasField(Object obj, String fieldName) {
        return null != getField(obj, fieldName);
    }

    /**
     * 获取父类属性
     */
    private static Field getSuperField(Class<?> c, String fieldName) {
        Field field = null;
        if (c.getSuperclass() != null) {
            try {
                field = c.getSuperclass().getDeclaredField(fieldName);
            } catch (Exception e) {
                field = getSuperField(c.getSuperclass(), fieldName);
            }
        }
        return field;
    }

    /**
     * 检测有没有继承父类
     * 
     * @param c
     *            类
     * @return 结果
     */
    public static boolean hasSuper(Class<?> c) {
        Class<?> s = c.getSuperclass();
        return s != null && !"Object".equals(s.getSimpleName());
    }

    /**
     * 是否为普通属性（原始类型、封装类型、字符串类型）
     */
    public static boolean isFlat(Field f) {
        return isPrimit(f) || isString(f) || isWrapType(f);
    }

    /**
     * 是否为原始类型
     */
    public static boolean isPrimit(Field f) {
        return f.getType().isPrimitive();
    }

    /**
     * 是否为封装类型
     */
    private static boolean isWrapType(Field f) {
        return isTypes(f, "Boolean", "Character", "Integer", "Long", "Short",
                "Byte", "Float", "Double");
    }

    /**
     * 是否为字符串类型
     */
    public static boolean isString(Field f) {
        return isType(f, "String");
    }

    /**
     * 判断类型
     * 
     * @param f
     *            属性
     * @param type
     *            类型
     * @return 结果
     */
    public static boolean isType(Field f, String type) {
        return type.equalsIgnoreCase(f.getType().getSimpleName());
    }

    /**
     * 检测对象类型
     */
    public static boolean isType(Object o, String type) {
        return type.equals(o.getClass().getSimpleName());
    }

    /**
     * 是否为集合类型
     * 
     * @param f
     *            属性
     * @return 结果
     */
    public static boolean isCollect(Field f) {
        return isTypes(f, "Set", "List");
    }

    /**
     * 类型是否为types中的一个
     * 
     * @param f
     *            属性
     * @param types
     *            类型
     * @return 结果
     */
    private static boolean isTypes(Field f, String... types) {
        for (int i = 0; i < types.length; i++) {
            if (isType(f, types[i])) {
                return true;
            }
        }
        return false;
    }

    /**
     * 是否为日期类型
     * 
     * @param f
     *            属性
     * @return 结果
     */
    public static boolean isDate(Field f) {
        return isType(f, "Date");
    }

    /**
     * 读取对象的属性值
     * 
     * @param o
     *            对象
     * @param propName
     *            属性名
     * @return 属性值
     */
    public static Object readValue(Object o, String propName) {
        Object result = null;
        try {
            result = getField(o, propName).get(o);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 将值写入对象的属性中
     * 
     * @param o
     *            对象
     * @param propName
     *            属性名
     * @param value
     *            值
     */
    public static void writeValue(Object o, String propName, Object value) {
        try {
            Field dstField = getField(o, propName);
            if (dstField != null) {
                dstField.set(o, autoMatchField(dstField, value));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 自动匹配目标属性
     */
    private static Object autoMatchField(Field dst, Object value) {
        if (value == null) {
            return value;
        }
        if (isType(dst, "Long") && !isType(value, "Long")) {
            value = DataUtil.toLong(value);
        }
        return value;
    }

    /**
     * 检查是否为静态属性
     * 
     * @param f
     *            属性
     * @return 结果
     */
    public static boolean isStatic(Field f) {
        return Modifier.isStatic(f.getModifiers());
    }

    /**
     * 创建一个对象实例
     */
    public static <T> T newObject(Class<T> c) {
        T t = null;
        try {
            t = c.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return t;
    }

    /**
     * 创建一个带参数的对象实例
     */
    public static <T> T newObject(Class<T> c, Object... params) {
        T t = null;
        try {
            Class<?>[] cs = (Class<?>[]) Array.newInstance(Class.class,
                    params.length);
            for (int i = 0; i < params.length; i++) {
                cs[i] = wrap2Primit(params[i].getClass());
            }
            t = c.getConstructor(cs).newInstance(params);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return t;
    }

    /**
     * 封装类型转为基本类型
     */
    private static Class<?> wrap2Primit(Class<?> wrap) {
        if (!baseTypes.containsB(wrap)) {
            return wrap;
        }
        return baseTypes.getA(wrap);
    }

    /**
     * 调用对象方法
     */
    public static void invoke(Object o, String method) {
        try {
            new BeanInvoker<Object>().invoke(o, method);
        } catch (Exception e) {
            BaseUtil.log.error("方法调用失败！ " + o.getClass().getName() + "::"
                    + method);
        }
    }

    /**
     * 调用对象带参方法
     */
    public static void invoke(Object o, String method, Object... values) {
        invoke(getBeanInvoker(values), o, method);
    }

    /**
     * 静态调用对象带参方法
     */
    public static void invoke(Class<?> c, String method, Object... values) {
        invoke(getBeanInvoker(values), c, method);
    }

    /**
     * 获取方法调用器
     */
    private static BeanInvoker<Object> getBeanInvoker(Object... values) {
        BeanInvoker<Object> invoker = new BeanInvoker<Object>();
        Class<?>[] classes = new Class<?>[values.length];
        for (int i = 0; i < classes.length; i++) {
            classes[i] = values[i].getClass();
        }
        invoker.setParamTypes(classes);
        invoker.setParamValues(values);
        return invoker;
    }

    /**
     * 执行调用
     */
    private static void invoke(BeanInvoker<Object> bi, Object o, String method) {
        try {
            bi.invoke(o, method);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 执行调用
     */
    private static void invoke(BeanInvoker<Object> bi, Class<?> c, String method) {
        try {
            bi.invoke(c, method);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 静态调用对象方法
     */
    public static void invoke(Class<?> c, String method) {
        try {
            new BeanInvoker<Object>().invoke(c, method);
        } catch (Exception e) {
            BaseUtil.log.error("静态方法调用失败！ " + c.getName() + "::" + method);
        }
    }

    /**
     * 获取对象类名
     */
    public static String getName(Object o) {
        return o.getClass().getSimpleName();
    }
}
